# **********************************************************
# Copyright (c) 2010-2014 Google, Inc.    All rights reserved.
# Copyright (c) 2009-2010 VMware, Inc.    All rights reserved.
# **********************************************************

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.

include(../make/policies.cmake NO_POLICY_SCOPE)

###########################################################################
# sources and targets

# we have no exe's here, and we want our dll's in the lib dir
# (could use MODULE instead of SHARED if it would let us link)
set(EXECUTABLE_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
set_per_config_ouput_to_match_single_config()

set(asm_deps
  "${PROJECT_SOURCE_DIR}/core/arch/asm_defines.asm"
  "${PROJECT_BINARY_DIR}/configure.h")
add_asm_target(arch/${ARCH_NAME}/${ARCH_NAME}.asm arch_core_asm_src arch_core_asm_tgt
  "_core" "" "${asm_deps}")
add_asm_target(arch/asm_shared.asm shared_core_asm_src shared_core_asm_tgt
  "_core" "" "${asm_deps}")
set(arch_core_asm_src ${arch_core_asm_src} ${shared_core_asm_src})
set(arch_core_asm_tgt ${arch_core_asm_tgt} ${shared_core_asm_tgt})
add_asm_target(arch/${ARCH_NAME}/${ARCH_NAME}.asm arch_noncore_asm_src arch_noncore_asm_tgt
  "_noncore" "-DNOT_DYNAMORIO_CORE_PROPER" "${asm_deps}")
add_asm_target(arch/asm_shared.asm shared_noncore_asm_src shared_noncore_asm_tgt
  "_noncore" "-DNOT_DYNAMORIO_CORE_PROPER" "${asm_deps}")
set(arch_noncore_asm_src ${arch_noncore_asm_src} ${shared_noncore_asm_src})
set(arch_noncore_asm_tgt ${arch_noncore_asm_tgt} ${shared_noncore_asm_tgt})
add_asm_target(arch/pre_inject_asm.asm preinject_asm_src preinject_asm_tgt ""
  "-DNOT_DYNAMORIO_CORE_PROPER" "${asm_deps}")

set(CORE_SRCS
  options.c
  dynamo.c
  fragment.c
  fcache.c
  link.c
  dispatch.c
  emit.c
  utils.c
  config.c
  stats.c
  heap.c
  monitor.c
  perfctr.c
  vmareas.c
  rct.c
  hotpatch.c
  hashtable.c
  module_list.c
  moduledb.c
  perscache.c
  nudge.c
  synch.c
  buildmark.c
  loader_shared.c
  io.c
  native_exec.c
  lib/instrument.c
  translate.c
  annotations.c
  )

if (UNIX)
  # i#46: Private string routines for libc independence.  On Windows our
  # bootstrapping loader finds these imports in ntdll.  If that becomes
  # problematic, we can find a way to compile this on Windows.
  set (CORE_SRCS ${CORE_SRCS} string.c)
endif (UNIX)

if (WIN32 AND NOT X64)
  # PR 219380: to avoid __ftol2_sse from libc
  # FIXME: There is no supported way to suppress a "command line" warning
  # used to do:
  #   $(BUILD)/io.obj: FILTER_CC = 2>&1 | $(GREP) -v "D9035 : option 'QIfist'";
  # we just live with it for now:
  #   [ 20%] Building C object core/CMakeFiles/dynamorio.dir/io.c.obj
  #   cl : Command line warning D9035 : option 'QIfist' has been deprecated and will be removed in a future release
  # Note that /QIfist causes casts to round instead of truncating (i#763)
  # which we work around in our code.
  set_source_files_properties(io.c COMPILE_FLAGS "/QIfist")
  set_source_files_properties(utils.c COMPILE_FLAGS "/QIfist")
endif (WIN32 AND NOT X64)

set(DECODER_SRCS
  arch/opnd_shared.c
  arch/${ARCH_NAME}/opnd.c
  arch/instr_shared.c
  arch/${ARCH_NAME}/instr.c
  arch/instrlist.c
  arch/decode_shared.c
  arch/${ARCH_NAME}/decode.c
  arch/encode_shared.c
  arch/${ARCH_NAME}/encode.c
  arch/disassemble_shared.c
  arch/${ARCH_NAME}/disassemble.c
  )
if (X86)
  set(DECODER_SRCS ${DECODER_SRCS}
    arch/${ARCH_NAME}/decode_table.c
    arch/${ARCH_NAME}/decode_fast.c)
else ()
  set(DECODER_SRCS ${DECODER_SRCS}
    arch/${ARCH_NAME}/table_a32_pred.c
    arch/${ARCH_NAME}/table_a32_unpred.c
    arch/${ARCH_NAME}/table_a32_encode.c)
endif ()

set(ARCH_SRCS
  arch/arch.c
  arch/emit_utils_shared.c
  arch/${ARCH_NAME}/emit_utils.c
  ${DECODER_SRCS}
  arch/interp.c
  arch/proc_shared.c
  arch/${ARCH_NAME}/proc.c
  arch/mangle_shared.c
  arch/${ARCH_NAME}/mangle.c
  arch/${ARCH_NAME}/clean_call_opt.c
  arch/steal_reg.c
  arch/x86_code.c
  ${arch_core_asm_src}
  arch/${ARCH_NAME}/optimize.c
  arch/sideline.c
  arch/retcheck.c
  )

if (X64)
  set(ARCH_SRCS ${ARCH_SRCS} arch/${ARCH_NAME}/x86_to_x64.c)
endif (X64)

# These custom commands are per-subdir so can't just place at top level
include(../make/CMake_events.cmake)
include(../make/ntdll_imports.cmake)

# i#801: For generated headers, we need to use add_dependencies to ensure that
# they are generated before compiling the other sources.  Listing the header as
# a source in the target isn't enough because it's really the source files
# themselves that need the header.  We can't put this in CMake_events.cmake b/c
# we end up w/ duplicate targets then.  Note that libutil/ uses these same
# targets but core is always enabled.
add_custom_target(generate_syslog DEPENDS "${SYSLOG_SRCS}")
if (WIN32)
  add_custom_target(generate_events DEPENDS "${EVENTS_SRCS}")
endif (WIN32)
function (add_gen_events_deps target)
  add_dependencies(${target} generate_syslog)
  if (WIN32)
    add_dependencies(${target} generate_events)
  endif ()
endfunction (add_gen_events_deps)

# Just like i#801, we can't put custom targets in ntdll_imports.cmake
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  # for correct parallel builds we need a target to avoid
  # duplicate and racy VS per-project rules
  add_custom_target(ntimp_tgt DEPENDS "${ntimp_lib}")
endif ()

if (UNIX)
  set(OSNAME unix)
  set(OS_SRCS
    unix/os.c
    unix/signal.c
    unix/module.c
    unix/pcprofile.c
    unix/stackdump.c
    unix/diagnost.c
    unix/loader.c
    )
  if (LINUX)
    set(OS_SRCS ${OS_SRCS} unix/memquery_linux.c)
    set(OS_SRCS ${OS_SRCS} unix/memquery.c)
    set(OS_SRCS ${OS_SRCS} unix/memcache.c)
    set(OS_SRCS ${OS_SRCS} unix/module_elf.c)
    set(OS_SRCS ${OS_SRCS} unix/ksynch_linux.c)
    set(OS_SRCS ${OS_SRCS} unix/tls_linux_${ARCH_NAME}.c)
    set(OS_SRCS ${OS_SRCS} unix/signal_linux.c)
    set(OS_SRCS ${OS_SRCS} unix/signal_linux_${ARCH_NAME}.c)
    set(OS_SRCS ${OS_SRCS} unix/native_elf.c)
    # XXX i#1286: should be split into nudge_linux.c and nudge_macos.c
    set(OS_SRCS ${OS_SRCS} unix/nudgesig.c)
  elseif (APPLE)
    set(OS_SRCS ${OS_SRCS} unix/memquery_macos.c)
    set(OS_SRCS ${OS_SRCS} unix/memquery.c)
    set(OS_SRCS ${OS_SRCS} unix/module_macho.c)
    set(OS_SRCS ${OS_SRCS} unix/ksynch_macos.c)
    set(OS_SRCS ${OS_SRCS} unix/tls_macos.c)
    set(OS_SRCS ${OS_SRCS} unix/signal_macos.c)
    set(OS_SRCS ${OS_SRCS} unix/native_macho.c)
    # XXX i#1286: implement nudge_macos.c
  elseif (VMKERNEL)
    set(VMKUW_DIR ${PROJECT_SOURCE_DIR}/../internal/core/linux)
    include_directories(${VMKUW_DIR})
    set(OS_SRCS ${OS_SRCS} ${VMKUW_DIR}/vmkuw.c)
    set(OS_SRCS ${OS_SRCS} unix/memquery_emulate.c)
    set(OS_SRCS ${OS_SRCS} unix/memcache.c)
    set(OS_SRCS ${OS_SRCS} unix/module_elf.c)
    set(OS_SRCS ${OS_SRCS} unix/ksynch_linux.c)
    set(OS_SRCS ${OS_SRCS} unix/signal_linux.c)
  endif ()
  set(PRELOAD_NAME drpreload)
  set(PRELOAD_SRCS
    unix/preload.c
    config.c
    unix/os.c
    ${arch_noncore_asm_src}
    # i#1334: private string routines for libc independence
    string.c
    io.c
    )
  set(INJECTOR_SRCS
    unix/injector.c
    config.c    # to read config
    unix/os.c  # for file_t interface
    string.c
    io.c
    ${arch_noncore_asm_src}  # for dynamorio_syscall
    )
  if (APPLE)
    set(INJECTOR_SRCS ${INJECTOR_SRCS} unix/module_macho.c)
  else (APPLE)
    set(INJECTOR_SRCS ${INJECTOR_SRCS} unix/module_elf.c)
  endif ()
else (UNIX)
  set(OSNAME win32)
  set(OS_SRCS
    win32/eventlog.c
    win32/os.c
    win32/syscall.c
    win32/callback.c
    win32/drmarker.c
    win32/ntdll.c
    win32/inject.c
    win32/inject_shared.c
    win32/module.c
    win32/module_shared.c
    win32/native_pe.c
    win32/diagnost.c
    win32/aslr.c
    win32/loader.c
    win32/drwinapi/drwinapi.c
    win32/drwinapi/ntdll_redir.c
    win32/drwinapi/kernel32_redir.c
    win32/drwinapi/kernel32_proc.c
    win32/drwinapi/kernel32_lib.c
    win32/drwinapi/kernel32_mem.c
    win32/drwinapi/kernel32_file.c
    win32/drwinapi/kernel32_sync.c
    win32/drwinapi/kernel32_misc.c
    win32/drwinapi/rpcrt4_redir.c
    win32/drwinapi/advapi32_redir.c
    win32/resources.rc
    ${ntimp_dep}
   )
  set(PRELOAD_SRCS
    win32/pre_inject.c
    win32/ntdll.c
    win32/inject_shared.c
    win32/drmarker.c
    ${arch_noncore_asm_src}
    ${preinject_asm_src}
    win32/module_shared.c
    win32/resources.rc
    config.c
    win32/os.c
    io.c
    )
  set(PRELOAD_NAME drpreinject)
  set(INJECTOR_SRCS
    win32/injector.c
    win32/inject.c
    win32/ntdll.c
    win32/inject_shared.c
    win32/module_shared.c
    ${arch_noncore_asm_src}
    win32/resources.rc
    config.c
    win32/os.c
    io.c
    ${ntimp_dep}
    )

  add_library(drearlyhelp1 SHARED
    win32/early_inject_helper1.c
    win32/resources.rc
    )
  add_library(drearlyhelp2 SHARED
    win32/early_inject_helper2.c
    win32/resources.rc
    )
endif (UNIX)

add_library(dynamorio ${SHARED_OR_STATIC}
  ${CORE_SRCS} ${ARCH_SRCS} ${OS_SRCS}
  # FIXME: any other way to add a dependence?  this is for export_list,
  # where the PRE_LINK custom command doesn't support DEPENDS.
  # Apparently just files w/ certain extensions are pulled out of here
  # for automatic rules.
  ${CMAKE_CURRENT_SOURCE_DIR}/lib/genapi.pl
  # ditto: adding here for dependence
  ${CMAKE_CURRENT_SOURCE_DIR}/CMake_ldscript.cmake
  )
set_target_properties(dynamorio PROPERTIES
  LIBRARY_OUTPUT_DIRECTORY${location_suffix} "${DR_LIBRARY_OUTPUT_DIRECTORY}"
  RUNTIME_OUTPUT_DIRECTORY${location_suffix} "${DR_LIBRARY_OUTPUT_DIRECTORY}"
  ARCHIVE_OUTPUT_DIRECTORY${location_suffix} "${DR_LIBRARY_OUTPUT_DIRECTORY}")
add_gen_events_deps(dynamorio)
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  # for parallel build correctness we need a target dependence
  add_dependencies(dynamorio ${arch_core_asm_tgt} ntimp_tgt)
endif ()

if (UNIX)
  # We always use a script for our own library bounds (PR 361594).
  # We could build this at configure time instead of build time as
  # it does not depend on the source files.
  # FIXME: this is duplicated in DynamoRIOConfig.cmake
  set(ld_script ${CMAKE_CURRENT_BINARY_DIR}/ldscript)
  set_directory_properties(PROPERTIES
    ADDITIONAL_MAKE_CLEAN_FILES "${ld_script}")

  function (set_preferred_base_start_and_end target)
    if (APPLE)
      set(ldflags "-image_base ${preferred_base}")
    elseif (NOT LINKER_IS_GNU_GOLD)
      set(ldflags "-Wl,${ld_script_option},\"${ld_script}\"")
      add_custom_command(TARGET ${target}
        PRE_LINK
        COMMAND ${CMAKE_COMMAND}
        # script does not inherit any vars so we must pass them all in
        # to work around i#84 be sure to put a space after -D for 1st arg at least
        ARGS -D outfile=${ld_script}
             -DCMAKE_LINKER=${CMAKE_LINKER}
             -DCMAKE_COMPILER_IS_GNUCC=${CMAKE_COMPILER_IS_GNUCC}
             -DLD_FLAGS=${LD_FLAGS}
             -Dset_preferred=${SET_PREFERRED_BASE}
             -Dreplace_maxpagesize=${REPLACE_MAXPAGESIZE}
             -Dpreferred_base=${preferred_base}
             -P ${PROJECT_SOURCE_DIR}/core/CMake_ldscript.cmake
        VERBATIM # recommended: p260
        )
    else ()
      set(ldflags "")
      if (SET_PREFERRED_BASE)
        # FIXME: This should be -Ttext-segment for bfd, but most golds want -Ttext.
        # See http://sourceware.org/ml/binutils/2013-02/msg00194.html
        set(ldflags "-Wl,-Ttext=${preferred_base}")
      endif ()
      # Add our start and end symbols for library bounds.
      set(ldflags "${ldflags} -Wl,--defsym,dynamorio_so_start=__executable_start")
      set(ldflags "${ldflags} -Wl,--defsym,dynamorio_so_end=end")
    endif ()
    append_property_string(TARGET ${target} LINK_FLAGS "${ldflags}")
  endfunction (set_preferred_base_start_and_end)

endif (UNIX)

# rather than a separate BUILD_INCLUDES option we always build
# include headers if building core (or if building docs or samples as
# they have real dependencies)
if (CLIENT_INTERFACE OR APP_EXPORTS)
  add_dependencies(dynamorio api_headers)
endif ()

add_library(${PRELOAD_NAME} SHARED ${PRELOAD_SRCS})
# We do not issue:
#   target_link_libraries(${PRELOAD_NAME} dynamorio)
# b/c preload dynamically loads dynamorio by name and does not have a
# static dependence.  Plus, by having a static dependence we hit issues
# like PR 258636.  We also don't set rpath b/c that only applies to executables.
if (UNIX)
  # we keep in {debug,release} subdir
  set(preload_dest "${DR_LIBRARY_OUTPUT_DIRECTORY}")
else (UNIX)
  # we keep in lib{32,64} base dir
  set(preload_dest "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
endif (UNIX)
set_target_properties(${PRELOAD_NAME} PROPERTIES
  LIBRARY_OUTPUT_DIRECTORY${location_suffix} "${preload_dest}"
  RUNTIME_OUTPUT_DIRECTORY${location_suffix} "${preload_dest}"
  ARCHIVE_OUTPUT_DIRECTORY${location_suffix} "${preload_dest}")
add_gen_events_deps(${PRELOAD_NAME})
if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  add_dependencies(${PRELOAD_NAME} ${arch_noncore_asm_tgt} ${preinject_asm_tgt})
endif ()

# static decoding library
add_library(drdecode
  ${DECODER_SRCS}
  arch/${ARCH_NAME}/mangle.c
  arch/decodelib.c
  ${arch_noncore_asm_src}
  )
set_target_properties(drdecode PROPERTIES
  COMPILE_FLAGS "-DNOT_DYNAMORIO_CORE_PROPER -DSTANDALONE_DECODER")
if (UNIX)
  append_property_string(TARGET drdecode COMPILE_FLAGS "-fPIC")
endif (UNIX)
if (WIN32 AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
  # for parallel build correctness we need a target dependence
  add_dependencies(drdecode ${arch_noncore_asm_tgt})
endif ()
add_gen_events_deps(drdecode)

###########################################################################
# linux build customization

if (UNIX)
  # FIXME case 69/1891: -z initfirst = initialize first at runtime (before libc)
  set_target_properties(${PRELOAD_NAME} PROPERTIES
    LINK_FLAGS "-nostartfiles")

  set(dynamorio_link_flags "")

  # Do non-lazy runtime binding
  if (APPLE)
    set(dynamorio_link_flags "${dynamorio_link_flags} -Xlinker -bind_at_load")
  else (APPLE)
    set(dynamorio_link_flags "${dynamorio_link_flags} -Xlinker -z -Xlinker now")
  endif (APPLE)

  if (APPLE AND NOT X64)
    # XXX i#1322: get the asm code PIC.  Until then we have to suppress the
    # error from ld about text relocs.
    set(dynamorio_link_flags "-read_only_relocs suppress")
  endif ()

  # Bind global symbols internally to avoid interposing
  if (APPLE)
    # Looks like -interposable is off by default
  else (APPLE)
    set(dynamorio_link_flags "${dynamorio_link_flags} -Xlinker -Bsymbolic")
  endif (APPLE)

  set(dynamorio_link_flags "${dynamorio_link_flags} -nostdlib")

  if (STATIC_LIBRARY)
    # We build static DR as PIC in case we're linked into a .so or a PIE.
    append_property_string(TARGET dynamorio COMPILE_FLAGS "-fPIC")
  else ()
    # i#47: set the ELF header entry point to _start for early injection.
    if (APPLE)
      set(dynamorio_link_flags "${dynamorio_link_flags} -Wl,-e,_start")
    else (APPLE)
      set(dynamorio_link_flags "${dynamorio_link_flags} -Wl,--entry,_start")
    endif (APPLE)
  endif ()

  if (NOT HAVE_FVISIBILITY)
    set(export_list ${CMAKE_CURRENT_BINARY_DIR}/exportlist)
    add_custom_command(TARGET dynamorio
      PRE_LINK
      COMMAND ${PERL_EXECUTABLE}
      ARGS ${CMAKE_CURRENT_SOURCE_DIR}/lib/genapi.pl
           # we assume ">" will work in all supported shells
           -filter "${defines}" > ${export_list}
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
      VERBATIM # recommended: p260
      )
    set(dynamorio_link_flags
      "${dynamorio_link_flags} -Xlinker --version-script -Xlinker ${export_list}")
  endif (NOT HAVE_FVISIBILITY)

  if (APPLE)
    # Needs explicit -init (regardless of name: _init, init, etc.)
    set(dynamorio_link_flags "${dynamorio_link_flags} -init __init")
  endif (APPLE)

  set_target_properties(dynamorio PROPERTIES
    LINK_FLAGS "${dynamorio_link_flags}"
    # See i#1375 comments
    INSTALL_NAME_DIR "@rpath")

  # This appends to LINK_FLAGS, so do it after we set them above.
  set_preferred_base_start_and_end(dynamorio)
  # i#46: We're pretty libc independent these days, but we still support
  # -no_private_loader when loaded by the system loader.  We also have a sqrt
  # import that we should cut.
  target_link_libraries(dynamorio dl m)

  # We need to do extra work to hide symbols in the static library build.
  # First we do a partial link with ld -r, which makes a single libdynamorio.o
  # object file.  Then we use objcopy --localize-hidden to hide all
  # non-exported symbols.
  if (STATIC_LIBRARY)
    if (NOT EXISTS ${CMAKE_OBJCOPY})
      message(FATAL_ERROR "STATIC_LIBRARY requires objcopy")
    endif ()
    get_target_property(dynamorio_dot_a dynamorio LOCATION)
    string(REGEX REPLACE "\\.a$" ".o" dynamorio_dot_o "${dynamorio_dot_a}")
    string(REPLACE " " ";" partial_link_flags "${CMAKE_C_FLAGS}")  # Get -m32/64
    set (globalize_pic_thunks)
    if (NOT X64)
      # Localizing PIC thunks on ia32 creates references to discarded sections.
      # XXX: Hardcoding this list of symbols is fragile.  It is known to work
      # with gcc 4.4 and 4.6.  gcc 4.7 uses symbols starting with __x86.
      set (globalize_pic_thunks ${CMAKE_OBJCOPY}
        --globalize-symbol=__i686.get_pc_thunk.bx
        --globalize-symbol=__i686.get_pc_thunk.cx
        --globalize-symbol=__x86.get_pc_thunk.bx
        --globalize-symbol=__x86.get_pc_thunk.cx
        "${dynamorio_dot_o}" &&
        )
    endif ()
    add_custom_command(TARGET dynamorio POST_BUILD
      COMMAND
      ${CMAKE_C_COMPILER} ${partial_link_flags} -fPIC
        -nostartfiles -nodefaultlibs -r
        -Wl,--whole-archive "${dynamorio_dot_a}" -Wl,--no-whole-archive
        -o "${dynamorio_dot_o}" &&
      ${CMAKE_OBJCOPY} --localize-hidden "${dynamorio_dot_o}" &&
      ${globalize_pic_thunks}
      rm -f "${dynamorio_dot_a}" &&
      ${CMAKE_AR} cr "${dynamorio_dot_a}" "${dynamorio_dot_o}" &&
      ${CMAKE_RANLIB} "${dynamorio_dot_a}"
      WORKING_DIRECTORY ${DR_LIBRARY_OUTPUT_DIRECTORY}
      VERBATIM)
  endif ()

  # We use .so versioning via symlinks.
  # We do not version the preload lib as it does not expose an API,
  # and drconfig is Windows-only.
  # Our releases match our API so we use major.minor as our soversion.
  # That allows both forward and backward compatibility, but means that
  # we need to provide symlinks for all backward compatible versions.
  # In the future CMake is expected to add explicit support for this.

  function (generate_soversions verstart verstop)
    while (${verstart} LESS ${verstop})
      math(EXPR COMPAT_MAJOR "${verstart} / 100")
      math(EXPR COMPAT_MINOR "${verstart} % 100")
      get_target_property(curver_base dynamorio LOCATION)
      get_filename_component(curver_path "${curver_base}" PATH)
      get_filename_component(curver_base "${curver_base}" NAME)
      get_target_property(curver_sover dynamorio SOVERSION)
      set(curver "${curver_base}.${curver_sover}")
      string(REGEX REPLACE "so.${VERSION_MAJOR_MINOR}"
        "so.${COMPAT_MAJOR}.${COMPAT_MINOR}" oldver "${curver}")
      set(COMPAT_SYMLINKS ${COMPAT_SYMLINKS} ${curver_path}/${oldver})
      add_custom_command(TARGET dynamorio POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E create_symlink ${curver} ${oldver}
        WORKING_DIRECTORY ${DR_LIBRARY_OUTPUT_DIRECTORY}
        VERBATIM)
      math(EXPR verstart "${verstart} + 1")
    endwhile ()
    set(COMPAT_SYMLINKS ${COMPAT_SYMLINKS} PARENT_SCOPE)
  endfunction (generate_soversions)

  string(REGEX REPLACE
    "^([0-9]+\\.[0-9]+).*" "\\1" VERSION_MAJOR_MINOR "${VERSION_NUMBER}")
  if (NOT APPLE) # FIXME i#1374: versioning causing problems
    set_target_properties(dynamorio PROPERTIES
      VERSION "${VERSION_MAJOR_MINOR}" SOVERSION "${VERSION_MAJOR_MINOR}")
    if (NOT STATIC_LIBRARY)
      # We assume OLDEST_COMPATIBLE_VERSION and cur ver have the same major #.
      # If they don't we need additional calls here.
      generate_soversions(${OLDEST_COMPATIBLE_VERSION} ${VERSION_NUMBER_INTEGER})
    endif ()
  endif (NOT APPLE)

  if (LINUX) # No checks for MacOS
    # Note that we can't locate readelf inside CMake_readelf.cmake as that
    # would disallow pointing externally at a readelf location, so we make it
    # a proper cache variable.
    # Note that nothing depends on CMake_readelf.cmake: that's ok, we'll check
    # in the next clean build if we modify the script.
    # readelf is a binutils app just like ld
    get_filename_component(binutils_path ${CMAKE_LINKER} PATH)
    if (APPLE)
      set(readelf_name "greadelf")
    else (APPLE)
      set(readelf_name "readelf")
    endif (APPLE)
    find_program(READELF_EXECUTABLE ${readelf_name} HINTS "${binutils_path}"
      DOC "path to readelf")
    if (STATIC_LIBRARY)
      # Skipping readelf check for STATIC_LIBRARY.
    elseif (NOT READELF_EXECUTABLE)
      message("${readelf_name} not found: not checking SElinux or execstack")
    else ()
      get_target_property(lib_to_check dynamorio LOCATION)
      add_custom_command(TARGET dynamorio
        POST_BUILD
        COMMAND ${CMAKE_COMMAND}
        # to work around i#84 be sure to put a space after -D for 1st arg at least
        ARGS -D lib=${lib_to_check}
             -D READELF_EXECUTABLE=${READELF_EXECUTABLE}
             -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_readelf.cmake
        VERBATIM # recommended: p260
        )
      get_target_property(lib_to_check ${PRELOAD_NAME} LOCATION)
      add_custom_command(TARGET ${PRELOAD_NAME}
        POST_BUILD
        COMMAND ${CMAKE_COMMAND}
        # to work around i#84 be sure to put a space after -D for 1st arg at least
        ARGS -D lib=${lib_to_check}
             -D READELF_EXECUTABLE=${READELF_EXECUTABLE}
             -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_readelf.cmake
        VERBATIM # recommended: p260
        )
    endif ()
  endif ()

  # we used to run size on libdynamorio.so for info purposes

  set_target_properties(${PRELOAD_NAME} PROPERTIES
    COMPILE_FLAGS "-DNOT_DYNAMORIO_CORE_PROPER -DRC_IS_PRELOAD")

  if (APPLE)
    # Linker complains about unresolved symbols.
    # This means we can't use preload by itself -- but these days we never do that.
    target_link_libraries(${PRELOAD_NAME} dynamorio)
    # Needs explicit -init (regardless of name: _init, init, etc.)
    append_property_string(TARGET ${PRELOAD_NAME} LINK_FLAGS "-init __init")
  endif (APPLE)

else (UNIX)
###########################################################################
# windows build customization

  set_target_properties(dynamorio PROPERTIES
    # Set define parameters for resources.rc.
    # Since it's hard to pass spaces in defines (I can get a " through using
    # \\\\\\\" but no spaces) we put strings in resources.rc and select
    # using RC_ defines.
    COMPILE_DEFINITIONS "RC_IS_CORE;INCLUDE_EVENTS"
    # i#921: we do not want a __chkstk function comparing our stack to
    # TEB.StackLimit, esp on win8 where the stack is high, so we disable
    # compiler-inserted stack probes globally in DR itself.
    COMPILE_FLAGS "/Gs65536")

  if ("${CMAKE_GENERATOR}" MATCHES "Ninja" AND
      "${CMAKE_VERSION}" VERSION_LESS "2.8.9.20120822")
    # cmake bug http://www.cmake.org/Bug/view.php?id=13486 causes rc.exe to
    # fail b/c it's passed COMPILE_FLAGS
    message(FATAL_ERROR "cmake at least version 2.8.9.20120822 is required for Ninja")
  endif ()

  set(NOLIBC_DLL_ENTRY /entry:DllMain)

  # Set up .def file.  Since there's only one and it does not depend on the
  # sources we can do this at configure time and not require a separate script.
  if (X64)
    set(DEF_SRC lib/dr_ntdll_x64.def)
  else (X64)
    set(DEF_SRC lib/dr_ntdll.def)
  endif (X64)
  set(DEF_BLD ${CMAKE_CURRENT_BINARY_DIR}/dr_ntdll.def)
  file(READ ${DEF_SRC} string)
  if (NOT CLIENT_INTERFACE)
    # we're not exporting snprintf() so remove from def file */
    string(REGEX REPLACE
      " snprintf"
      "" string "${string}")
  endif (NOT CLIENT_INTERFACE)
  file(WRITE ${DEF_BLD} "${string}")
  set(FORWARD_TO_NTDLL "/def:\"${DEF_BLD}\" /ignore:4197")

  # PR 219380: we export ntdll routines from dynamorio.dll by forwarding them.
  # However, the linker tries to be smart and thinks our _snprintf forward
  # is the same as our own snprintf: it removes the latter and issues a warning.
  # We re-add our own snprintf by listing it in the .def file, and we suppress
  # the warning using the secret option "/ignore".
  set(dynamorio_link_flags /NODEFAULTLIB)
  if (X64)
    # Xref PR 215395, we currently require DynamoRIO to be loaded in a certain address
    # range.  To be double sure we compile it FIXED for now.
    set(dynamorio_link_flags "${dynamorio_link_flags} /dynamicbase:no /FIXED")
  endif (X64)
  if (DEBUG)
    set(LD_FLAGS "${LINK_DBG} /release /opt:ref")
  else (DEBUG)
    set(LD_FLAGS "${LINK_DBG} /release /opt:ref /opt:icf")
  endif (DEBUG)
  # XXX i#893: VS2012 won't build any target using x86.asm w/ safeseh
  set(LD_FLAGS "${LD_FLAGS} /safeseh:no")
  if (SET_PREFERRED_BASE)
    set(dynamorio_link_flags "${dynamorio_link_flags} /base:${preferred_base}")
  endif (SET_PREFERRED_BASE)
  if (X64)
    set(LD_FLAGS "${LD_FLAGS} /machine:X64")
  else (X64)
    set(LD_FLAGS "${LD_FLAGS} /machine:I386")
  endif (X64)
  set(dynamorio_link_flags
    "${LD_FLAGS} ${ntimp_flags} ${dynamorio_link_flags} /dll /incremental:no")
  set(dynamorio_link_flags
    "${dynamorio_link_flags} ${NOLIBC_DLL_ENTRY} ${FORWARD_TO_NTDLL}")
  # cmake does /out, /implib, and /pdb for us, but we do want map file
  get_target_property(drout dynamorio LOCATION${location_suffix})
  get_filename_component(drpath ${drout} PATH)
  get_filename_component(drname ${drout} NAME_WE)
  set(dynamorio_link_flags
    "${dynamorio_link_flags} /map:\"${drpath}/${drname}.map\" /mapinfo:exports")
  # export functions in x86.asm where we can't just say __declspec(dllexport))
  set(dynamorio_link_flags
    "${dynamorio_link_flags} /export:dynamo_auto_start /export:dynamorio_app_take_over")
  if (APP_EXPORTS)
    set(dynamorio_link_flags
      "${dynamorio_link_flags} /export:dr_app_start /export:dr_app_take_over")
    set(dynamorio_link_flags
      "${dynamorio_link_flags} /export:dr_app_running_under_dynamorio")
  endif (APP_EXPORTS)
  if (CLIENT_INTERFACE)
    set(dynamorio_link_flags "${dynamorio_link_flags} /export:dr_try_start")
    set(dynamorio_link_flags "${dynamorio_link_flags} /export:dr_call_on_clean_stack")
    if (NOT X64)
      set(dynamorio_link_flags "${dynamorio_link_flags} /export:dr_invoke_x64_routine")
    endif (NOT X64)
  endif (CLIENT_INTERFACE)
  if (WINDOWS)
    set(dynamorio_link_flags
      "${dynamorio_link_flags} /export:dynamorio_earliest_init_takeover")
  endif (WINDOWS)

  if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 18.0)
    # i#1376: with VS2013 we end up with an import of "_except1" from msvcr*.dll,
    # regardless of flags like QIfist, /fp:except-, /fp:fast, or /Qfast_transcendentals.
    # Our solution is to not link with msvcrt.lib and rely on ntdll having _aulldvrm.
    # This means that dynamorio.dll built with VS2013 won't run on 2K or NT.

    # case 4125: we link with msvcrt.lib for vc8 /O2's use of _alldvrm
    # note that _alldvrm is in ntdll.dll on xp+; but for pre-xp uses we
    # stick with the static linking of the impl in msvcrt.lib.
    # We must link in custom flags and not target_link_libraries() b/c we need
    # to be AFTER our ntdll_imports.lib.
    set(dynamorio_link_flags "${dynamorio_link_flags} msvcrt.lib")
  endif ()

  set_target_properties(dynamorio PROPERTIES LINK_FLAGS "${dynamorio_link_flags}")

  if (DEBUG)
    set(WIN32_C_LIB libcmtd)
  else (DEBUG)
    set(WIN32_C_LIB libcmt)
  endif (DEBUG)

  if (NOT STATIC_LIBRARY)
    # ensure there are no dependencies other than ntdll
    find_program(DUMPBIN_EXECUTABLE dumpbin.exe DOC "path to dumpbin.exe")
    if (DUMPBIN_EXECUTABLE)
      add_custom_command(TARGET dynamorio POST_BUILD
        COMMAND ${CMAKE_COMMAND}
        ARGS -D lib=${drout}
             -D DUMPBIN_EXECUTABLE=${DUMPBIN_EXECUTABLE}
             -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_checkdeps.cmake
        VERBATIM)
    else ()
      message(STATUS "Cannot find dumpbin so not performing dependence check")
    endif ()
  else ()
    # i#975: if we're building static, none of those link flags above are going
    # to make it through.  But target_link_libraries will.  We name a C library
    # here to make sure things get linked in the right order to avoid duplicate
    # definitions.  See the case 4125 msvcrt comment above about why we need a C
    # library.
    target_link_libraries(dynamorio ${WIN32_C_LIB} ${ntimp_lib})
  endif ()


  set_target_properties(${PRELOAD_NAME} PROPERTIES
    COMPILE_FLAGS "-DNOT_DYNAMORIO_CORE_PROPER -DRC_IS_PRELOAD")
  set(pre_link_flags
    # preinjector requires no C library for its unload-self method
    # not bothering with map files
    "${LD_FLAGS} /dll /incremental:no /base:0x14000000 /NODEFAULTLIB ${NOLIBC_DLL_ENTRY}")
  set_target_properties(${PRELOAD_NAME} PROPERTIES LINK_FLAGS "${pre_link_flags}")
  # case 4125: we link with msvcrt.lib for vc8 /O2's use of _alldvrm
  target_link_libraries(${PRELOAD_NAME} ${ntimp_lib} kernel32 msvcrt dynamorio)


  set_target_properties(drearlyhelp1 PROPERTIES
    COMPILE_FLAGS "-DRC_IS_EARLY1")
  # base = (release base - 64k)
  set_target_properties(drearlyhelp1 PROPERTIES
    # not bothering with map files
    LINK_FLAGS "${LD_FLAGS} /dll /NODEFAULTLIB /base:0x70FF0000 ${NOLIBC_DLL_ENTRY}")
  target_link_libraries(drearlyhelp1 drearlyhelp2)

  set_target_properties(drearlyhelp2 PROPERTIES
    COMPILE_FLAGS "-DRC_IS_EARLY2")
  # base = (helper1 base - 64k)
  set_target_properties(drearlyhelp2 PROPERTIES
    # not bothering with map files
    LINK_FLAGS "${LD_FLAGS} /dll /NODEFAULTLIB /base:0x71110000 ${NOLIBC_DLL_ENTRY}")

endif (UNIX)

# drinjectlib
add_library(drinjectlib SHARED ${INJECTOR_SRCS})
add_gen_events_deps(drinjectlib)
target_link_libraries(drinjectlib drdecode)
set_target_properties(drinjectlib PROPERTIES
  # COMPILE_DEFINITONS isn't working for me: cmake bug?
  # Set define parameters for resources.rc
  COMPILE_FLAGS "-DNOT_DYNAMORIO_CORE_PROPER -DRC_IS_DRINJECTLIB"
  # XXX i#1375: if we move to cmake 2.8.12 we can use its built-in
  # rpath: until then we add @rpath ourselves.  The downside is that
  # everyone who uses this lib has to have the rpath set and can't
  # just copy the lib to the local dir (should we document that?
  # DynamoRIO_RPATH is on for standalone, which covers most uses of
  # drconfiglib and drinjectlib, the only libs we do this for).
  INSTALL_NAME_DIR "@rpath")
if (WIN32)
  if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
    # for parallel build correctness we need a target dependence
    add_dependencies(drinjectlib ${arch_noncore_asm_tgt} ntimp_tgt)
  endif ()
  set(drinjectlib_link_flags
    # not bothering with map files
    "${LD_FLAGS} /incremental:no /subsystem:console /NODEFAULTLIB")
  # If we use ${ntimp_flags} in the link flags and thus prior to the
  # target_link_libraries() list, on VS2005 and VS2008 we end up with duplicate
  # symbols atoi and iswctype from libcmtd and ntdll.  Unlike dynamorio.dll we're
  # ok w/ libc syms so we re-order and take the static syms first.
  set_target_properties(drinjectlib PROPERTIES LINK_FLAGS "${drinjectlib_link_flags}")
  # I'm adding libcmt to attempt to make sure we have multithreaded C lib:
  target_link_libraries(drinjectlib drdecode ${WIN32_C_LIB} ${ntimp_lib}
    kernel32 advapi32 imagehlp)
  # drinject.exe needs a copy in the bin dir
  add_custom_command(TARGET drinjectlib POST_BUILD COMMAND ${CMAKE_COMMAND}
    ARGS -E copy "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/drinjectlib.dll"
    "${EXECUTABLE_OUTPUT_DIRECTORY}/" VERBATIM)
endif (WIN32)

if (ANNOTATIONS)
  set(BUILD_ANNOTATION "${BUILD_INCLUDE}/annotations")
  set(BUILD_ANNOTATION "${BUILD_INCLUDE}/annotations" PARENT_SCOPE)
  file(MAKE_DIRECTORY ${BUILD_ANNOTATION})
  if (NOT WINDOWS OR NOT X64)
    configure_file("${PROJECT_SOURCE_DIR}/third_party/valgrind/valgrind.h"
      "${BUILD_ANNOTATION}/valgrind.h" COPYONLY)
    configure_file("${PROJECT_SOURCE_DIR}/third_party/valgrind/memcheck.h"
      "${BUILD_ANNOTATION}/memcheck.h" COPYONLY)
  endif (NOT WINDOWS OR NOT X64)
  configure_file("${PROJECT_SOURCE_DIR}/core/lib/dr_annotations_asm.h"
    "${BUILD_ANNOTATION}/dr_annotations_asm.h" COPYONLY)
  configure_file("${PROJECT_SOURCE_DIR}/core/lib/dr_annotations.h"
    "${BUILD_ANNOTATION}/dr_annotations.h" COPYONLY)
  configure_file("${PROJECT_SOURCE_DIR}/core/lib/dr_annotations.c"
    "${BUILD_ANNOTATION}/dr_annotations.c" COPYONLY)

  include_directories("${BUILD_ANNOTATION}")
endif ()

if (BUILD_TESTS)
  add_executable(unit_tests unit_tests.c
    ${CORE_SRCS} ${ARCH_SRCS} ${OS_SRCS})
  add_gen_events_deps(unit_tests)
  if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
    # for parallel build correctness we need a target dependence
    add_dependencies(unit_tests ${arch_core_asm_tgt} ntimp_tgt)
  endif ()
  set_target_properties(unit_tests PROPERTIES
    COMPILE_FLAGS "-DRC_IS_TEST -DSTANDALONE_UNIT_TEST"
    RUNTIME_OUTPUT_DIRECTORY${location_suffix} "${EXECUTABLE_OUTPUT_DIRECTORY}")
  if (UNIX)
    # i#1228 make sure entry point of the elf binary pointing to _start.
    # Otherwise, the entry point will point to the start of .text section,
    # which might not be _start.
    append_property_string(TARGET unit_tests LINK_FLAGS "-Wl,--entry,_start")
    target_link_libraries(unit_tests c dl m pthread)
    set_preferred_base_start_and_end(unit_tests)
  else (UNIX)
    # Just like drinjectlib (see above) we need libc before ntimp_lib
    target_link_libraries(unit_tests ${WIN32_C_LIB} ${ntimp_lib})
    set_target_properties(unit_tests PROPERTIES
      LINK_FLAGS "/base:${preferred_base} ${LD_FLAGS}")
  endif (UNIX)
  add_test(unit_tests "${EXECUTABLE_OUTPUT_DIRECTORY}/unit_tests")
endif (BUILD_TESTS)

###########################################################################

# Do not put core/ into the include dirs as our link.h will conflict
# with /usr/include/link.h!
include_directories(BEFORE
  ${CMAKE_CURRENT_SOURCE_DIR}/lib
  ${CMAKE_CURRENT_SOURCE_DIR}/arch
  ${CMAKE_CURRENT_SOURCE_DIR}/${OSNAME}
  ${CMAKE_CURRENT_SOURCE_DIR}/arch/${ARCH_NAME}
  )

# Avoid transitive linking for imported target.
# This is a shared library, after all, and ntdll.lib is typically not available.
set_target_properties(dynamorio PROPERTIES
  # I seem to have to set these to remove the
  # IMPORTED_LINK_INTERFACE_LIBRARIES from the resulting .cmake file.
  LINK_INTERFACE_LIBRARIES ""
  LINK_INTERFACE_LIBRARIES_NOCONFIG ""
  # Setting these isn't affecting anything but doing so anyway.
  IMPORTED_LINK_INTERFACE_LIBRARIES ""
  IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG "")
install_exported_target(dynamorio ${INSTALL_LIB}
  # CMake doesn't set +x on shared libraries, so we have to ask for it.
  PERMISSIONS ${owner_access} OWNER_EXECUTE GROUP_READ GROUP_EXECUTE)
DR_export_target(dynamorio)
install_exported_target(drinjectlib ${INSTALL_LIB_BASE})
DR_export_target(drinjectlib)
if (UNIX)
  DR_install(TARGETS ${PRELOAD_NAME} DESTINATION ${INSTALL_LIB})
  DR_install(FILES ${COMPAT_SYMLINKS} DESTINATION ${INSTALL_LIB})
else (UNIX)
  # we put drinjectlib into bin for use by our tools
  DR_install(TARGETS drinjectlib DESTINATION ${INSTALL_BIN})
  DR_install(TARGETS ${PRELOAD_NAME} RUNTIME DESTINATION ${INSTALL_LIB_BASE})
  DR_install(TARGETS drearlyhelp1 RUNTIME DESTINATION ${INSTALL_LIB_BASE})
  DR_install(TARGETS drearlyhelp2 RUNTIME DESTINATION ${INSTALL_LIB_BASE})
endif (UNIX)

install_exported_target(drdecode ${INSTALL_LIB})

# We need separate 32-bit and 64-bit versions so we put into lib dir
DR_install(FILES ${ntimp_lib} DESTINATION ${INSTALL_LIB_BASE})
